package cn.scnu.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.scnu.common.constant.NormalConstants;
import cn.scnu.common.util.GisUtil;
import cn.scnu.common.util.NotImportEmptyRowListener;
import cn.scnu.mapper.BaiyunProjectListMapper;
import cn.scnu.model.dto.*;
import cn.scnu.model.entity.BaiyunProjectListEntity;
import cn.scnu.model.enums.KeyNameEnum;
import cn.scnu.model.query.KeyQueryParam;
import cn.scnu.service.IBaiyunProjectListService;
import com.alibaba.excel.EasyExcel;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author hqzqaq
 * @since 2024-12-18
 */
@Service
@Slf4j
@RequiredArgsConstructor
@DS("master_2")
public class BaiyunProjectListServiceImpl extends ServiceImpl<BaiyunProjectListMapper, BaiyunProjectListEntity> implements IBaiyunProjectListService {

    private final BaiyunProjectListMapper mapper;

    @Value("${town.ywhjList}")
    private List<String> ywhjList;

    @Value("${shp.path}")
    private String shpPath;

    @Override
    public List<Integer> getYears() {
        return mapper.getYears();
    }

    @Override
    public BaseKeyValueDTO<Integer, Double> getContractCountAndAmountByYear(Integer year) {
        return mapper.getContractCountAndAmountByYear(year);
    }

    @Override
    public List<BaseKeyValueDTO<Integer, Integer>> getContractCountChangeByYear(Integer year) {
        return mapper.getContractCountChangeByYear(year);
    }

    @Override
    public List<CountAndAmountForScbkDTO> getContractCountAndAmountForScbk(Integer year) {
        return mapper.getContractCountAndAmountForScbk(year);
    }

    @Override
    public List<String> getUniqueScbk(Integer year) {
        List<String> uniqueScbk = mapper.getUniqueScbk(year);
        if (!uniqueScbk.isEmpty()) {
            uniqueScbk.add(NormalConstants.ALL);
        }
        return uniqueScbk;
    }

    @Override
    public List<BaseKeyValueDTO<String, Integer>> getContractCountForYwlx(Integer year, String scbk) {
        return mapper.getContractCountForYwlx(year, scbk);
    }

    @Override
    public List<BaseKeyValueDTO<String, Double>> getContractAmountForYwlx(Integer year, String scbk) {
        return mapper.getContractAmountForYwlx(year, scbk);
    }

    @Override
    public List<ContractMonthAmountForYwlxByScbkDTO> getContractMonthAmountForYwlxByScbk(Integer year, String scbk, String ywlx) {
        List<ContractMonthAmountForYwlxByScbkDTO> contractMonthAmountForYwlxByScbk = mapper.getContractMonthAmountForYwlxByScbk(year, scbk, ywlx);
        Map<Integer, ContractMonthAmountForYwlxByScbkDTO> dtoMap = contractMonthAmountForYwlxByScbk.stream()
                .collect(Collectors.toMap(ContractMonthAmountForYwlxByScbkDTO::getMonth, dto -> dto, (existing, replacement) -> existing));

        List<ContractMonthAmountForYwlxByScbkDTO> result = new ArrayList<>();
        for (int month = 1; month <= 12; month++) {
            result.add(dtoMap.getOrDefault(month, new ContractMonthAmountForYwlxByScbkDTO(month, 0, 0.0)));
        }
        return result;
    }

    @Override
    public List<String> getUniqueYwlxByScbk(Integer year, String scbk) {
        List<String> uniqueYwlxByScbk = mapper.getUniqueYwlxByScbk(year, scbk);
        if (!uniqueYwlxByScbk.isEmpty()) {
            uniqueYwlxByScbk.add(NormalConstants.ALL);
        }
        return uniqueYwlxByScbk;
    }

    @Override
    public Double getReceiveAmount(Integer year) {
        return mapper.getReceiveAmount(year);
    }

    @Override
    public List<BaseKeyValueDTO<Integer, Double>> getMonthReceiveAmount(Integer year) {
        List<BaseKeyValueDTO<Integer, Double>> monthCounts = mapper.getMonthReceiveAmount(year);
        return buildMonthCount(monthCounts, () -> 0.0);
    }

    @NotNull
    private <K extends Number, V> List<BaseKeyValueDTO<K, V>> buildMonthCount(List<BaseKeyValueDTO<K, V>> monthCounts, Supplier<V> defaultValueSupplier) {

        Map<K, BaseKeyValueDTO<K, V>> monthCountMap = monthCounts.stream()
                .collect(Collectors.toMap(BaseKeyValueDTO::getKey, dto -> dto, (existing, replacement) -> existing));

        List<BaseKeyValueDTO<K, V>> result = new ArrayList<>();
        for (int month = 1; month <= 12; month++) {
            // 使用正确的类型转换，并处理可能的Number类型
            K monthKey = (K) Double.valueOf(month);

            result.add(monthCountMap.getOrDefault(monthKey, new BaseKeyValueDTO<>(monthKey, defaultValueSupplier.get())));
        }
        return result;
    }

    @Override
    public List<BaseKeyValueDTO<String, Double>> getReceiveAmountForScbk(Integer year) {
        return mapper.getReceiveAmountForScbk(year);
    }

    @Override
    public List<BaseKeyValueDTO<String, Double>> getReceiveAmountForYelxByScbk(Integer year, String scbk) {
        return mapper.getReceiveAmountForYelxByScbk(year, scbk);
    }

    @Override
    public List<BaseKeyValueDTO<Integer, Double>> getMonthReceiveAmountForYelxByScbk(Integer year, String scbk, String ywlx) {
        List<BaseKeyValueDTO<Integer, Double>> monthCounts = mapper.getMonthReceiveAmountForYelxByScbk(year, scbk, ywlx);
        return buildMonthCount(monthCounts, () -> 0.0);
    }

    @Override
    public Double getProductionAmount(Integer year) {
        return mapper.getProductionAmount(year);
    }

    @Override
    public List<BaseKeyValueDTO<String, Double>> getProductionAmountForScbk(Integer year) {
        return mapper.getProductionAmountForScbk(year);
    }

    @Override
    public List<BaseKeyValueDTO<Integer, Double>> getMonthProductionAmount(Integer year) {
        List<BaseKeyValueDTO<Integer, Double>> monthCounts = mapper.getMonthProductionAmount(year);
        return buildMonthCount(monthCounts, () -> 0.0);
    }

    @Override
    public List<BaseKeyValueDTO<String, Double>> getProductionAmountForYelxByScbk(Integer year, String scbk) {
        return mapper.getProductionAmountForYelxByScbk(year, scbk);
    }

    @Override
    public List<BaseKeyValueDTO<Integer, Double>> getMonthProductionAmountForYelxByScbk(Integer year, String scbk, String ywlx) {
        List<BaseKeyValueDTO<Integer, Double>> monthCounts = mapper.getMonthProductionAmountForYelxByScbk(year, scbk, ywlx);
        return buildMonthCount(monthCounts, () -> 0.0);
    }

    @Override
    public DataDashProjectDTO getProjectIndex(Integer year) {
        return mapper.getProjectIndex(year);
    }

    @Override
    public List<BaseKeyValueDTO<Integer, Integer>> getMonthProjectNum(Integer year) {
        return buildMonthCount(mapper.getMonthProjectNum(year), () -> 0);
    }

    @Override
    public List<ProjectCountAndAmountForScbkDTO> getProjectCountAndAmountForScbk(Integer year) {
        return mapper.getProjectCountAndAmountForScbk(year);
    }

    @Override
    public List<ProjectRaAndCaDTO> getProjectCountForYelxByScbk(Integer year, String scbk) {
        return mapper.getProjectCountForYelxByScbk(year, scbk);
    }

    @Override
    public List<NsfAndYsfForScbkDTO> getProjectNsfCountForYelxByScbk(Integer year, String scbk) {
        return mapper.getProjectNsfCountForYelxByScbk(year, scbk);
    }

    @Override
    public List<String> getUniqueZj() {
        List<BaiyunTownNameDTO> townNameDTOS = GisUtil.getAttrFromGeoJson(ResourceUtil.getStream(shpPath), BaiyunTownNameDTO.class);
        List<String> townNameList = townNameDTOS.stream().map(BaiyunTownNameDTO::getTownName).collect(Collectors.toList());
        townNameList.add(NormalConstants.ALL);
        return townNameList;
    }

    @Override
    public List<ProjectCountAndAmountForYwlxAndYwhjDTO> getProjectIndexForYwlxByTown(Integer year, String townName) {
        List<ProjectCountAndAmountForYwlxAndYwhjDTO> res = new ArrayList<>();

        for (String ywhj : ywhjList) {
            List<String> ywlxList = mapper.getUniqueYwlxByYwhj(year, townName, ywhj);
            List<ProjectCountAndAmountForYwlxDTO> projectIndexForYwlxByTown = mapper.getProjectIndexForYwlxByTown(year, townName, ywhj, ywlxList);

            // 创建一个Map来检查ywlxList中的值是否在projectIndexForYwlxByTown中
            Map<String, ProjectCountAndAmountForYwlxDTO> projectIndexMap = projectIndexForYwlxByTown.stream()
                    .collect(Collectors.toMap(ProjectCountAndAmountForYwlxDTO::getYwlx, dto -> dto));
            List<ProjectCountAndAmountForYwlxDTO> projectIndexList = new ArrayList<>();
            for (String ywlx : ywlxList) {
                ProjectCountAndAmountForYwlxDTO projectCountAndAmountForYwlxDTO = projectIndexMap.getOrDefault(ywlx, new ProjectCountAndAmountForYwlxDTO(0, 0, ywlx, 0.0, 0.0));
                projectIndexList.add(projectCountAndAmountForYwlxDTO);
            }

            ProjectCountAndAmountForYwlxAndYwhjDTO projectCountAndAmountForYwlxAndYwhjDTO = new ProjectCountAndAmountForYwlxAndYwhjDTO();
            projectCountAndAmountForYwlxAndYwhjDTO.setYwhj(ywhj);
            projectCountAndAmountForYwlxAndYwhjDTO.setProjectCountAndAmountForYwlxDTOList(projectIndexList);
            res.add(projectCountAndAmountForYwlxAndYwhjDTO);
        }

        return res;
    }

    @Override
    public List<ProjectDataFormDTO> getKeyList(KeyQueryParam keyQueryParam) {
        return mapper.getKeyList(keyQueryParam);
    }

    @SneakyThrows
    @Override
    public void exportKeyResultExcel(KeyQueryParam keyQueryParam, HttpServletResponse response) {
        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("UTF-8");
        String fileName = keyQueryParam.getKeyNameField().getComment() + System.currentTimeMillis() + ".xlsx";
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        List<ProjectDataFormDTO> keyList = mapper.getKeyList(keyQueryParam);
        // 导出excel
        // 创建 Excel 写入器
        EasyExcel.write(response.getOutputStream(), ProjectDataFormDTO.class)
                .head(head(keyQueryParam.getKeyNameField()))
                .sheet(keyQueryParam.getKeyNameField().getComment())
                .doWrite(keyList);
    }

    @Override
    @SneakyThrows
    @Transactional
    public Integer importBaiyunProjectExcel(MultipartFile file) {
        // 要求数据覆盖数据库中的baiyun_project_list_copy表中的数据
        mapper.delete(new QueryWrapper<>());

        // 使用easyexcel解析excel文件，包含每一个sheet
        InputStream inputStream = file.getInputStream();

        EasyExcel.read(inputStream, BaiyunProjectListEntity.class, new NotImportEmptyRowListener(mapper)).doReadAll();

        // List<BaiyunProjectListEntity> BaiyunProjectListEntity = EasyExcel.read(inputStream, BaiyunProjectListEntity.class, null).doReadAllSync();
        return 1;
    }

    @Override
    public List<BaseKeyValueDTO<Integer, Integer>> getMonthProjectCaseCount(Integer year) {
        return buildMonthCount(mapper.getMonthProjectCaseCount(year), () -> 0);
    }

    @Override
    public List<BaseKeyValueDTO<Integer, Double>> getMonthProjectPlannedCharge(Integer year) {
        return buildMonthCount(mapper.getMonthProjectPlannedCharge(year), () -> 0.0);
    }

    @Override
    public List<BaseKeyValueDTO<Integer, Double>> getContractAmountChangeByYear(Integer year) {
        return mapper.getContractAmountChangeByYear(year);
    }

    @Override
    @SneakyThrows
    public void exportTownProjectStatsByBusinessPhase(Integer year, String townName, HttpServletResponse response) {
        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("UTF-8");
        String fileName = townName + System.currentTimeMillis() + ".xlsx";
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        List<ProjectDataFormDTO> keyList = mapper.getProjectForYwlxExport(year, townName, ywhjList);
        // 导出excel
        // 创建 Excel 写入器
        List<List<String>> list = new ArrayList<List<String>>();
        List<String> head0 = new ArrayList<String>();
        head0.add("业务环节");
        List<String> head1 = new ArrayList<String>();
        head1.add("业务类型");
        List<String> head2 = new ArrayList<String>();
        head2.add("入案");
        List<String> head3 = new ArrayList<String>();
        head3.add("出案");
        List<String> head4 = new ArrayList<String>();
        head4.add("拟收费");
        List<String> head5 = new ArrayList<String>();
        head5.add("已收费");
        list.add(head0);
        list.add(head1);
        list.add(head2);
        list.add(head3);
        list.add(head4);
        list.add(head5);
        EasyExcel.write(response.getOutputStream(), ProjectDataFormDTO.class)
                .head(list)
                .sheet(townName)
                .doWrite(keyList);
    }

    private List<List<String>> head(KeyNameEnum keyNameField) {
        List<List<String>> list = new ArrayList<List<String>>();
        List<String> head0 = new ArrayList<String>();
        head0.add(keyNameField.getComment());
        List<String> head1 = new ArrayList<String>();
        head1.add("业务类型");
        List<String> head2 = new ArrayList<String>();
        head2.add("入案");
        List<String> head3 = new ArrayList<String>();
        head3.add("出案");
        List<String> head4 = new ArrayList<String>();
        head4.add("拟收费");
        List<String> head5 = new ArrayList<String>();
        head5.add("已收费");
        list.add(head0);
        list.add(head1);
        list.add(head2);
        list.add(head3);
        list.add(head4);
        list.add(head5);
        return list;
    }
}
